package cn.emailChat.ai.service.scheduler;

import cn.emailChat.ai.domain.ContactProfile;
import cn.emailChat.ai.domain.ConversationSummary;
import cn.emailChat.ai.repo.ContactProfileRepo;
import cn.emailChat.ai.repo.ConversationSummaryRepo;
import cn.emailChat.ai.service.util.Summarizer;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @Description 定时任务定期扫描短期记忆，并以此提炼和保存长期记忆；手动关闭长时间未关闭的短期记忆
 * @Author susu
 * @Date 2025/8/20
 */

@Slf4j
@Service
@RequiredArgsConstructor
public class MemoryMaintenanceJob {
    private final ConversationSummaryRepo csRepo;
    private final ContactProfileRepo cpRepo;
    private final Summarizer summarizer; // 你的 DeepSeekSummarizer

    // ==== 方案B：@Value 配置 ====
    /** 周期性蒸馏的“活跃窗口”（天）——只对最近活跃的线程做增量提炼 */
    @Value("${memory.distill.active-window-days:7}")
    private int activeWindowDays;

    /** 同一线程两次蒸馏之间的最小间隔（分钟），用于防抖 */
    @Value("${memory.distill.min-interval-minutes:240}") // 4 小时
    private int minIntervalMinutes;

    /** 自动关闭阈值（天）：超过该时间未更新的开放线程将被自动关闭 */
    @Value("${memory.thread.stale-days:14}")
    private int staleDays;

    /** 每次任务处理的批大小，避免一次拉太多触发限流 */
    @Value("${memory.scheduler.batch-size:50}")
    private int batchSize;

    /** 调度：默认每 30 分钟运行一次（可改 cron） */
    @Scheduled(cron = "${memory.scheduler.cron:0 */30 * * * *}")
    @Transactional
    public void run() {
        try {
            distillActiveOpenThreads();
        } catch (Exception e) {
            log.warn("[MemoryMaintenanceJob] 蒸馏长期记忆出错（忽略本轮其余）: {}", e.toString(), e);
        }
        try {
            closeStaleThreads();
        } catch (Exception e) {
            log.warn("[MemoryMaintenanceJob] 自动关闭陈旧线程出错（忽略本轮其余）: {}", e.toString(), e);
        }
    }

    /** A. 对“开放且最近活跃”的线程做长期记忆蒸馏（周期性同步到 ContactProfile） */
    private void distillActiveOpenThreads() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime activeSince = now.minusDays(activeWindowDays);
        LocalDateTime resyncBefore = now.minusMinutes(minIntervalMinutes);

        List<ConversationSummary> batch = csRepo.findDistillCandidates(
                activeSince, resyncBefore, PageRequest.of(0, batchSize));

        if (batch.isEmpty()) {
            log.debug("[MemoryMaintenanceJob] 无可蒸馏候选（activeSince={}）", activeSince);
            return;
        }

        for (ConversationSummary cs : batch) {
            try {
                String distilled = summarizer.distillToLongTerm(cs.getSummary());
                if (distilled == null || distilled.isBlank()) {
                    // 即使为空也更新 last_profile_sync_at，避免抖动
                    cs.setLastProfileSyncAt(now);
                    csRepo.save(cs);
                    continue;
                }
                ContactProfile cp = cpRepo.findById(cs.getContactEmail().toLowerCase())
                        .orElseGet(() -> {
                            ContactProfile x = new ContactProfile();
                            x.setContactEmail(cs.getContactEmail().toLowerCase());
                            x.setLongTermSummary("");
                            return x;
                        });
                cp.setLongTermSummary(mergeLongTerm(cp.getLongTermSummary(), distilled));
                cpRepo.save(cp);

                cs.setLastProfileSyncAt(now);
                csRepo.save(cs);

            } catch (Exception ex) {
                log.warn("[MemoryMaintenanceJob] 蒸馏失败 threadId={}, err={}",
                        cs.getThreadId(), ex.toString());
            }
        }

        log.info("[MemoryMaintenanceJob] 蒸馏完成：本轮 {} 条", batch.size());
    }

    /** B. 自动关闭“长时间无更新”的开放线程，并做一次最终蒸馏 */
    private void closeStaleThreads() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime staleBefore = now.minusDays(staleDays);

        List<ConversationSummary> batch = csRepo.findStaleOpenThreads(
                staleBefore, PageRequest.of(0, batchSize));

        if (batch.isEmpty()) {
            log.debug("[MemoryMaintenanceJob] 无需自动关闭（staleBefore={}）", staleBefore);
            return;
        }

        for (ConversationSummary cs : batch) {
            try {
                // 最终蒸馏（若上次同步已很近，可跳过；这里稳妥起见仍同步一次）
                String distilled = summarizer.distillToLongTerm(cs.getSummary());
                if (distilled != null && !distilled.isBlank()) {
                    ContactProfile cp = cpRepo.findById(cs.getContactEmail().toLowerCase())
                            .orElseGet(() -> {
                                ContactProfile x = new ContactProfile();
                                x.setContactEmail(cs.getContactEmail().toLowerCase());
                                x.setLongTermSummary("");
                                return x;
                            });
                    cp.setLongTermSummary(mergeLongTerm(cp.getLongTermSummary(), distilled));
                    cpRepo.save(cp);
                }

                // 关闭线程
                cs.setStatus("closed");
                cs.setClosedAt(now);
                cs.setLastProfileSyncAt(now); // 记录一次最终同步时间
                csRepo.save(cs);

            } catch (Exception ex) {
                log.warn("[MemoryMaintenanceJob] 关闭失败 threadId={}, err={}",
                        cs.getThreadId(), ex.toString());
            }
        }

        log.info("[MemoryMaintenanceJob] 自动关闭完成：本轮 {} 条", batch.size());
    }

    // ==== 简易合并策略（可替换为更智能的去重/合并器） ====
    private String mergeLongTerm(String oldSum, String distilled) {
        if (oldSum == null || oldSum.isBlank()) return distilled == null ? "" : distilled.trim();
        if (distilled == null || distilled.isBlank()) return oldSum;
        if (oldSum.contains(distilled)) return oldSum;
        String merged = oldSum.trim();
        if (!merged.endsWith("。") && !merged.endsWith("；")) merged += "；";
        merged += distilled.trim();
        // 连续分号去重
        return merged.replaceAll("[；;]{2,}", "；");
    }
}
